import json
from django.shortcuts import render

# Create your views here.
"""
判断用户名是否重复的功能

前端： 用户在用户名框中输入后，点击别处，触发失去焦点的事件，发送axios（ajax）请求

后端：接受请求：接受用户名
      处理业务逻辑：根据用户名查询数据库，如果查询结果数量等于0说明没有注册，数量为1说明已经注册
      返回响应：返回json数据{"code":0, "count":0/1, "errmsg":"ok"}
      
    路由（restful风格） GET /usernames/<username>/count/
"""
from django.views import View
from django.http import HttpRequest,HttpResponse,JsonResponse
from apps.users.models import User
class usernamecountview(View):
  def get(self, request:HttpRequest, username):
    # 1. 接收用户名 username
    
    
    # 2. 根据用户名查询数据库
    count = User.objects.filter(username= username).count()
    # 3. 返回响应
    return JsonResponse({
      "code":0,
      "count":count,
      "errmsg":"ok",
    })
    pass

"""
判断手机号和判断用户名完全一样
"""

class MobileCountView(View):
    """判断手机号是否重复注册"""

    def get(self, request, mobile):
        """
        :param request: 请求对象
        :param mobile: 手机号
        :return: JSON
        """
        count = User.objects.filter(mobile=mobile).count()
        return JsonResponse({'code': 0, 'errmsg': 'OK', 'count': count})
  
"""

前端：当用户输入完用户名，密码，手机号等信息，并点击注册按钮时，将发送axios请求到后端
后端：接受请求（json), 获取数据
     验证数据（必须在后端再次校验，不能相信任何前端发过来的数据）
     - 用户名，密码，确认密码。手机号，用户是否同意协议等等都要有
     - 用户名符合规则，数据库中无数据
     - 密码符合规则，密码和确认密码一致
     - 手机号符合规则，数据库中无数据
     
     数据入库
     发送响应 json({
       "code":0.
       "errmsg":"ok",
     })
     
     路由 POST register/
"""
import re
class RegisterView(View):
    """用户注册"""

    def post(self, request:HttpRequest):

    #  1. 接受请求（json), 获取数据
    
      data_dict = json.loads(request.body)
      username = data_dict.get('username')
      password = data_dict.get('password')
      password2 = data_dict.get('password2')
      mobile = data_dict.get('mobile')
      allow = data_dict.get('allow')
      
    #  验证数据（必须在后端再次校验，不能相信任何前端发过来的数据）
    #  - 用户名，密码，确认密码。手机号，用户是否同意协议等等都要有
    #  - 用户名符合规则，数据库中无数据
    #  - 密码符合规则，密码和确认密码一致
    #  - 手机号符合规则，数据库中无数据
    
    
      # 判断参数是否齐全
      if not all([username, password, password2, mobile, allow]):
          return JsonResponse({'code':400, 'errmsg':'缺少必传参数!'})
            # 判断用户名是否是5-20个字符
      if not re.match(r'[a-zA-Z0-9_]{5,20}', username):
          return JsonResponse({'code': 400, 'errmsg': 'username格式有误!'})
      # 判断密码是否是8-20个数字
      if not re.match(r'[0-9A-Za-z]{8,20}', password):
          return JsonResponse({'code': 400, 'errmsg': 'password格式有误!'})
      # 判断两次密码是否一致
      if password != password2:
          return JsonResponse({'code': 400, 'errmsg': '两次输入不对!'})
      # 判断手机号是否合法
      if not re.match(r'1[3-9]\d{9}', mobile):
          return JsonResponse({'code': 400, 'errmsg': 'mobile格式有误!'})
      # 判断是否勾选用户协议
      if not allow:
          return JsonResponse({'code': 400, 'errmsg': 'allow格式有误!'})
    #  数据入库
    #  发送响应 json({
    #    "code":0.
    #    "errmsg":"ok",
    #  })
    
      # 1. 入库方案1
      # user = User(username=username,password=password,mobile=mobile)
      # user.save()
      
      # 2. 入库方案2
      # User.objects.create(username=username,password=password,mobile=mobile)
      
      # 方案1和2都可以，但是他们的入库后的密码是明文！，容易泄露，所以采用下面的方案
      user = User.objects.create_user(username=username,
                            password=password,
                            mobile=mobile)
      
      # try:
      #   user = User.objects.create_user(username=username,
      #                               password=password,
      #                               mobile=mobile)
      # except Exception as e:
      #     return JsonResponse({'code': 400, 'errmsg': 'reigster fail'})


      # django 为我们提供了状态保持的方法
      from django.contrib.auth import login
      
      ## request ，和 user参数， user是登录用户的信息，即我们刚刚注册的user
      login(request, user)

      return JsonResponse(
          {'code': 0, 'errmsg': '注册成功!'}
      )
      
      
      

"""

1. 如果需求是注册成功后即表示用户认证通过，那么此时可以在注册成功后实现状态保持
2. 如果需求是注册成功后不表示用户认证通过，那么此时不用在注册成功后实现状态保持

状态保持：客户端: cookie
          服务端: session
"""

"""
登陆：
前端：当用户填写了用户名和密码之后，点击登陆按钮，前端应该发送一个axios请求

后端：请求：接受数据，验证数据
      业务逻辑：验证用户名密码是否正确，session信息是否正确
      响应：返回 json 0成功 400失败
      
步骤：
1. 接收数据
2. 验证数据
3. 验证用户名密码是否正确
4. session
5. 判断是否记住登陆

"""

class LoginView(View):
    """用户登录"""
    def post(self, request:HttpRequest):
      # 1. 接收数据
      data = json.loads(request.body)
      username = data.get('username')
      password = data.get('password')
      remembered = data.get('remembered')
      # 2. 验证数据
      if not all([username, password]):
          return JsonResponse({'code': 400, 'errmsg': '缺少必传参数!'})
        
      ## 验证是用户是手机号登陆还是用户名登陆
      if re.match(r'1[3-9]\d{9}', username):
          # 手机号
          User.USERNAME_FIELD = 'mobile'
      else:
          # account 是用户名
          # 根据用户名从数据库获取 user 对象返回.
          User.USERNAME_FIELD = 'username'

      
      
      # 3. 验证用户名密码是否正确
      # 我们可以用用户名直接查询
      # User.objects.get(username = username)
      # 也可以用django提供的authenticate
      from django.contrib.auth import authenticate
      # authenticate 传递用户名和密码，如果正确返回user,否则返回None
      
      user = authenticate(username=username, password = password)
      
      if user is None:
        return JsonResponse({'code':400, 'errmsg':"用户名或密码错误"})
      # 4. session
      from django.contrib.auth import login
      login(request=request, user=user)
      # 5. 判断是否记住登陆
      if remembered:
        # 记住账密
        request.session.set_expiry(None)
        
      else:
        # 不记住账密
        request.session.set_expiry(0)
        
      
      
        
      response = JsonResponse({'code':0, 'errmsg':"ok"})
      response.set_cookie("username", username)
      from apps.carts.utils import merge_cart_cookie_to_redis
      response = merge_cart_cookie_to_redis(request=request, user=user, response=response)
      return response
    
    
"""
前端：点击退出按钮，发送axios delete请求
后端： 
"""

class LogoutView(View):
    """用户登出"""
    
    def delete(self, request:HttpRequest):
        # 1. 清除session
        from django.contrib.auth import logout
        logout(request)
        
        # 2. 清除cookie
        response = JsonResponse({'code':0, 'errmsg':"ok"})
        response.delete_cookie("username")
        
        return response
      
      
"""
用户是否登陆判断
LoginRequiredMixin 未登陆的用户会返回重定向，而不是json，我们要重写父类方法，让它返回json
并把这个封装到util中去调用

"""

from django.contrib.auth.mixins import LoginRequiredMixin
from utils.views import LoginRequiredJSONMixin
class UserInfoView(LoginRequiredJSONMixin, View):
    """用户中心"""

    def get(self, request:HttpRequest):
        # 获取界面需要的数据,进行拼接
        info_data = {
            'username': request.user.username,
            'mobile': request.user.mobile,
            'email': request.user.email,
            'email_active': request.user.email_active
        }

        # 返回响应
        return JsonResponse({'code':0,
                                  'errmsg':'ok',
                                  'info_data':info_data})
        
class emailview(LoginRequiredJSONMixin,View):
  def put(self, request:HttpRequest):
    # 1.接收请求
    data = json.loads(request.body)
    email = data.get('email')
    # 2.校验邮箱
    if not email:
      return JsonResponse({'code': 400, 'errmsg': '缺少必传参数!'})
    if not re.match(r'^[a-z0-9][\w.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
      return JsonResponse({'code': 400, 'errmsg': '邮箱格式错误!'})
    # 3，保存数据
    request.user.email = email
    request.user.save()
    # 4. 发送验证邮箱
    from django.core.mail import send_mail
    
    from apps.users.utils import generate_verify_email_url
    token = generate_verify_email_url(request.user)
    # html_msg = "点击<a href='http://www.meiduo.site:8080/success_verify_email.html?token=%s'>验证邮箱</a>" % token
    # 发送邮件
    
    to_email = request.user.email
    verify_url = 'http://www.meiduo.site:8080/success_verify_email.html?token=%s' % token
    html_msg = '<p>尊敬的用户您好！</p>' \
                   '<p>感谢您使用美多商城。</p>' \
                   '<p>您的邮箱为：%s 。请点击此链接激活您的邮箱：</p>' \
                   '<p><a href="%s">%s<a></p>' % (to_email, verify_url, verify_url)
                   
    from celery_tasks.email.tasks import celery_send_email
    celery_send_email.delay(html_msg)


    # 5. 返回
    return JsonResponse({'code': 0, 'errmsg': 'ok'})

class emailVerifyView(View):
  def put(self, request:HttpRequest):
    # 1. 接收token
    token = request.GET.get('token')
    if not token:
      return JsonResponse({'code': 400, 'errmsg': '缺少必传参数!'})
    # 2. 解密token
    from apps.users.utils import check_verify_email_token
    user = check_verify_email_token(token)
    if user is None:
      return JsonResponse({'code': 400, 'errmsg': '无效的token!'})
    # 3. 修改邮箱状态
    user.email_active = True
    user.save()
    return JsonResponse({'code': 0, 'errmsg': 'ok'})
  
  
  
"""
增删改查逻辑

请求
业务逻辑
响应

增：
1. 接收数据
2. 验证数据
3. 数据入库
4. 返回响应

删：
1. 查询到指定数据
2. 数据删除（物理删除 or 逻辑删除）
3. 返回响应

改：（个人的邮箱）
1. 查询指定的记录
2. 接收数据
3. 验证数据
4. 数据修改
5. 返回响应


查（个人中心的数据展示）：
1. 查询指定数据
2. 将对象数据转换为字典数据
3. 返回响应

"""
import re
from django.http import JsonResponse, HttpResponseBadRequest
from django.views import View
from django_redis import get_redis_connection
from apps.users.models import Address

class CreateAddressView(LoginRequiredJSONMixin, View):
    """新增地址"""

    def post(self, request:HttpRequest):

        """实现新增地址逻辑"""
        # 判断是否超过地址上限：最多20个
        # Address.objects.filter(user=request.user).count()
        count = request.user.addresses.count()
        
        if count >= 20:
            return JsonResponse({'code': 400, 'errmsg': '超过地址数量上限'})

        # 接收参数
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return HttpResponseBadRequest('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('参数mobile有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return HttpResponseBadRequest('参数email有误')

        # 保存地址信息
        
        address = Address.objects.create(
            user=request.user,
            title = receiver,
            receiver = receiver,
            province_id = province_id,
            city_id = city_id,
            district_id = district_id,
            place = place,
            mobile = mobile,
            tel = tel,
            email = email
        )

        # 设置默认地址
        if not request.user.default_address:
            request.user.default_address = address
            request.user.save()
  

        # 新增地址成功，将新增的地址响应给前端实现局部刷新
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }

        # 响应保存结果
        return JsonResponse({'code': 0, 'errmsg': '新增地址成功', 'address':address_dict})
      
    
class AddressView(View):
  """用户收货地址"""

  def get(self, request):
      """提供地址管理界面
      """
      # 获取所有的地址:
      addresses = Address.objects.filter(user=request.user,
                                          is_deleted=False)

      # 创建空的列表
      address_dict_list = []
      # 遍历
      for address in addresses:
          address_dict = {
              "id": address.id,
              "title": address.title,
              "receiver": address.receiver,
              "province": address.province.name,
              "city": address.city.name,
              "district": address.district.name,
              "place": address.place,
              "mobile": address.mobile,
              "tel": address.tel,
              "email": address.email
          }

          # 将默认地址移动到最前面
          default_address = request.user.default_address
          if default_address.id == address.id:
              # 查询集 addresses 没有 insert 方法
              address_dict_list.insert(0, address_dict)
          else:
              address_dict_list.append(address_dict)

      default_id = request.user.default_address_id

      return JsonResponse({'code': 0,
                            'errmsg': 'ok',
                            'addresses': address_dict_list,
                            'default_address_id': default_id})
      
      
"""
保存用户历史记录，只保存5条
"""
from apps.goods.models import SKU
from django_redis import get_redis_connection

class UserBrowseHistory(LoginRequiredJSONMixin, View):
    """用户浏览记录"""
    def get(self, request):
        """获取用户浏览记录"""
        # 获取Redis存储的sku_id列表信息
        redis_conn = get_redis_connection('history')
        sku_ids = redis_conn.lrange('history_%s' % request.user.id, 0, 4)

        # 根据sku_ids列表数据，查询出商品sku信息
        skus = []
        for sku_id in sku_ids:
            sku = SKU.objects.get(id=sku_id)
            skus.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price
            })

        return JsonResponse({'code': 0, 'errmsg': 'OK', 'skus': skus})
    def post(self, request):
    
        """保存用户浏览记录"""
        # 接收参数
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')

        # 校验参数
        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return JsonResponse({'code': 400, 'errmsg': 'sku不存在'})

        # 保存用户浏览数据
        redis_conn = get_redis_connection('history')
        pl = redis_conn.pipeline()
        user_id = request.user.id

        # 先去重
        pl.lrem('history_%s' % user_id, 0, sku_id)
        # 再存储
        pl.lpush('history_%s' % user_id, sku_id)
        # 最后截取
        pl.ltrim('history_%s' % user_id, 0, 4)
        # 执行管道
        pl.execute()

        # 响应结果
        return JsonResponse({'code': 0, 'errmsg': 'OK'})